package org.summerframework.component.upload.file.service.impl;

import org.summerframework.component.upload.DownloadInputSteam;
import org.summerframework.component.upload.FileUploadServerService;
import org.summerframework.component.upload.FileUploadService;
import org.summerframework.component.upload.UploadResultCode;
import org.summerframework.component.upload.exception.UploadFileException;
import org.summerframework.component.upload.file.service.FileService;
import org.summerframework.component.upload.file.service.FileUserRelService;
import org.summerframework.component.upload.file.service.dto.FileDTO;
import org.summerframework.component.upload.file.service.dto.FileUserRelDTO;
import org.summerframework.component.upload.image.service.FileImageService;
import org.summerframework.component.upload.image.service.dto.FileImageDTO;
import org.summerframework.component.upload.type.FileUploadDTO;
import org.summerframework.component.upload.util.ImageInfo;
import org.summerframework.component.upload.util.UploadFileUtil;
import org.summerframework.core.exception.SystemException;
import org.summerframework.core.exception.WebException;
import org.summerframework.core.jackson.DownloadUrlService;
import org.summerframework.core.util.CheckUtil;
import org.summerframework.core.util.Md5Util;
import org.summerframework.core.util.UrlUtil;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.IIOException;
import java.io.*;

/**
 * @author 石超
 * @version v1.0.0
 */
@Service
public class FileUploadServiceImpl implements FileUploadService, DownloadUrlService {
    private static final String specifications = "%sx%s";

    @Resource
    private FileService fileService;

    @Resource
    private UploadFileUtil uploadFileUtil;

    @Resource
    private FileUploadServerService fileUploadServerService;

    @Resource
    private FileUserRelService fileUserRelService;

    @Resource
    private FileImageService fileImageService;

    @Override
    public FileUserRelDTO uploadFile(FileUploadDTO fileUploadDTO) {
        if (fileUploadDTO.getFileBytes() == null && fileUploadDTO.getInputStream() == null) {
            throw new UploadFileException(UploadResultCode.ERROR_NOT_UPLOAD_FILE);
        }

        if (fileUploadDTO.getUserId() == null) {
            fileUploadDTO.setUserId(1L);
        }

        File file = null;
        FileUserRelDTO fileUserRelDTO = new FileUserRelDTO();

        try {
            file = File.createTempFile("tmp", "." + FilenameUtils.getExtension(fileUploadDTO.getFileName()));
            final File finalFile = file;

            if (fileUploadDTO.getFileBytes() == null) {// 图片上传
                try (InputStream inputStream = fileUploadDTO.getInputStream()) {
                    FileUtils.copyInputStreamToFile(inputStream, file);
                } catch (Exception e) {
                    throw new WebException(UploadResultCode.ERROR_NOT_UPLOAD_FILE, e);
                }
            } else {
                FileUtils.writeByteArrayToFile(file, fileUploadDTO.getFileBytes());
            }

            //根据文件MD5码判断文件是否已经上传
            String md5code = Md5Util.createMD5(file);
            FileDTO fileDTO = fileService.selectByMd5(md5code);

            boolean isNew = fileDTO == null || fileDTO.getId() == null;

            if (isNew) {
                fileDTO = new FileDTO();
                //新文件,按原来的方式上传,并处理生成相关文件
                fileDTO.setMd5(md5code);
                fileDTO.setName(fileUploadDTO.getName());
                fileDTO.setSuffix(fileUploadDTO.getExt());
                fileDTO.setSize(file.length());

                FileDTO finalFileDTO = fileDTO;

                uploadFileUtil.uploadFile(file, fileUploadDTO.getFileName(),
                        fileUploadDTO.getNeedHandle(), uploadFile -> {
                            finalFileDTO.setUrl(fileUploadServerService.uploadFile(uploadFile.getPath()));
                            //取前端传入的type
                            if (fileUploadDTO.getType() == null) {
                                finalFileDTO.setType(uploadFile.getType());
                            } else if ((uploadFile.getType().equals(1) && fileUploadDTO.getType().equals(4)) || fileUploadDTO.getType().equals(uploadFile.getType())) {// 按照指定类型上传 结果不一致时不予许上传,  当上传全景时uploadFile.type=1
                                finalFileDTO.setType(fileUploadDTO.getType());
                            } else {
                                throw new WebException(UploadResultCode.FILE_UPLOADIMAGE_NOT_FILE);
                            }

                            FileDTO insertResult = fileService.insert(finalFileDTO);

                            //新增用户关系后返回这个dto
                            fileUserRelDTO.setFileId(insertResult.getId());
                            fileUserRelDTO.setSuffix(finalFileDTO.getSuffix());
                            fileUserRelDTO.setType(finalFileDTO.getType());
                            fileUserRelDTO.setUserId(fileUploadDTO.getUserId());
                            fileUserRelDTO.setName(finalFileDTO.getName());
                            fileUserRelDTO.setUrl(finalFileDTO.getUrl());
                            fileUserRelDTO.setSize(finalFileDTO.getSize());
                            fileUserRelDTO.setType(finalFileDTO.getType());
                            fileUserRelService.insert(fileUserRelDTO);

                            if (fileUploadDTO.getNeedHandle()) {
                                //根据回调的内容写数据库
                                if (uploadFile.getType() == 1) {
                                    fileUserRelDTO.setFileImageDTO(createThumb(fileUploadDTO, finalFile, finalFileDTO.getId(), finalFileDTO.getUrl()));
                                }
                            }
                        });
            } else {
                //新增用户关系后返回这个dto
                fileUserRelDTO.setFileId(fileDTO.getId());
                fileUserRelDTO.setSuffix(fileDTO.getSuffix());

                if (fileUploadDTO.getType() != null) {
                    fileUserRelDTO.setType(fileUploadDTO.getType());
                } else {
                    fileUserRelDTO.setType(fileDTO.getType());
                }

                fileUserRelDTO.setUserId(fileUploadDTO.getUserId());
                fileUserRelDTO.setUrl(fileDTO.getUrl());
                fileUserRelDTO.setName(fileUploadDTO.getName());
                fileUserRelDTO.setSize(fileDTO.getSize());
                fileUserRelDTO.setType(fileDTO.getType());
                fileUserRelService.insert(fileUserRelDTO);

                if (fileUploadDTO.getNeedHandle()) {
                    //根据回调的内容写数据库
                    if (fileDTO.getType() == 1) {
                        final FileImageDTO fileImageDTO = fileImageService.selectBySpecifications(fileDTO.getId(), String.format(specifications, fileUploadDTO.getWidth(), fileUploadDTO.getHeight()));

                        if (fileImageDTO == null) {
                            fileUserRelDTO.setFileImageDTO(createThumb(fileUploadDTO, file, fileDTO.getId(), fileDTO.getUrl()));
                        } else {
                            fileUserRelDTO.setFileImageDTO(fileImageDTO);
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new WebException(UploadResultCode.ERROR_NOT_UPLOAD_FILE, e);
        } finally {
            FileUtils.deleteQuietly(file);
        }

        return fileUserRelDTO;
    }

    private FileImageDTO createThumb(FileUploadDTO fileUploadDTO, File file, long fileBaseId, String oldUrl) {
        FileImageDTO imageDTO = new FileImageDTO();
        File f = null;

        try (InputStream in = new FileInputStream(file)) {
            final ImageInfo imageInfo = new ImageInfo();
            imageInfo.setInput(in);
            imageInfo.setDetermineImageNumber(true);
            imageInfo.setCollectComments(false);

            if (fileUploadDTO.getNeedHandle() && imageInfo.check()) {
                if (imageInfo.getWidth() > fileUploadDTO.getWidth() || imageInfo.getHeight() > fileUploadDTO.getHeight()) {
//                    in.reset();
                    f = File.createTempFile("tmp", "." + imageInfo.getFormatName());
                    imageDTO.setSpecifications(String.format(specifications, fileUploadDTO.getWidth(), fileUploadDTO.getHeight()));

                    try (InputStream changeIn = new FileInputStream(file)) {
                        Thumbnails.of(changeIn).size(fileUploadDTO.getWidth(), fileUploadDTO.getHeight()).toFile(f);
                    }

                    imageDTO.setThumbnail(fileUploadServerService.uploadFile(f.getAbsolutePath()));
                } else {
                    imageDTO.setThumbnail(oldUrl);
                    imageDTO.setSpecifications(String.format(specifications, imageInfo.getWidth(), imageInfo.getHeight()));
                }

                imageDTO.setFileId(fileBaseId);
                imageDTO.setWidth(imageInfo.getWidth());
                imageDTO.setHeight(imageInfo.getHeight());
                fileImageService.insert(imageDTO);
            }
        } catch (IIOException e) {
            throw new SystemException(UploadResultCode.ERROR_UNSUPPORTED_IMAGE_TYPE, e);
        } catch (IOException e) {
            throw new SystemException(UploadResultCode.ERROR_UPLOAD_FAILURE, e);
        } finally {
            FileUtils.deleteQuietly(f);
        }

        return imageDTO;
    }

    @Override
    public DownloadInputSteam getInputSteam(Long id, String uploadFile) {
        FileUserRelDTO fileDTO = fileUserRelService.selectById(id);

        if (fileDTO != null) {
            return getInputSteam(fileDTO.getFileId(), fileDTO.getUrl(), fileDTO.getName() + "." + fileDTO.getSuffix(), uploadFile);
        }

        return getInputSteam((InputStream) null, "");
    }

    @Override
    public DownloadInputSteam getInputSteam(String path, String uploadFile) {
        try {
            FileDTO fileDTO = fileService.selectByUrl(path);

            if (fileDTO != null) {
                return getInputSteam(fileDTO.getId(), path, fileDTO.getName() + "." + fileDTO.getSuffix(), uploadFile);
            }

            return getInputSteam(fileUploadServerService.getInputStream(path), FilenameUtils.getName(path));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return getInputSteam((InputStream) null, "");
    }

    private DownloadInputSteam getInputSteam(Long fileId, String path, String fileName, String uploadFile) {
        try {
            if (uploadFile != null) {
                final FileImageDTO fileImageDTO = fileImageService.selectBySpecifications(fileId, uploadFile);

                if (fileImageDTO != null) {
                    return getInputSteam(fileUploadServerService.getInputStream(fileImageDTO.getThumbnail()), fileName);
                }
            }

            return getInputSteam(fileUploadServerService.getInputStream(path), fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return getInputSteam((InputStream) null, "");
    }

    private DownloadInputSteam getInputSteam(InputStream is, String name) {
        if (is != null) {
            return new DownloadInputSteam(is, name);
        }

        return new DownloadInputSteam(new ByteArrayInputStream(new byte[0]), name);
    }

    @Override
    public String getUrl(Long id) {
        FileUserRelDTO fileDTO = fileUserRelService.selectById(id);

        if (fileDTO != null) {
            return parseUrl(fileDTO.getUrl());
        } else {
            return UrlUtil.getUrl("default") + "/storage/" + id;
        }
    }

    @Override
    public String getUrl(String url) {
        FileDTO fileDTO = fileService.selectByUrl(url);

        if (fileDTO != null) {
            return parseUrl(fileDTO.getUrl());
        } else {
            return UrlUtil.getUrl("default") + url;
        }
    }

    private String parseUrl(String url) {
        final String parseUrl = fileUploadServerService.getUrl(url);

        if (CheckUtil.isUrl(parseUrl)) {
            return parseUrl;
        }

        return UrlUtil.getUrl("default") + url;
    }
}
